//
//  CoreDataRoutine.m
//  BallistaTimeline
//
//  Created by s.zalozniy on 3/27/13.
//  Copyright 2013 Mozi Development. All rights reserved.
//

#import "Utils.h"
#import "ProductManager.h"

#import "CoreDataRoutines.h"

@interface CoreDataRoutines ()

@property (nonatomic, retain, readwrite) NSManagedObjectModel *managedObjectModel;
-(void) fillDB;
-(void) managedObjectContextDidSave:(NSNotification *)notification;

@end


@implementation CoreDataRoutines


static CoreDataRoutines *sharedInstance = nil;


@synthesize managedObjectModel;
@synthesize managedObjectContext;


#pragma mark - Static methods

+(CoreDataRoutines *) sharedInstance {
    @synchronized (self) {
        if (!sharedInstance) {
            sharedInstance = [[CoreDataRoutines alloc] init];
        }
    }
	
	return sharedInstance;
}


#pragma mark - Instance initialization

-(id) init {
	self = [super init];
	if (!self) {
		return nil;
	}
    
    NSString *dbFilePath = [[Utils applicationCacheDirectory] stringByAppendingPathComponent: @"fr.sqlite"];
    BOOL dataBaseExists = [[NSFileManager defaultManager] fileExistsAtPath:dbFilePath];
	
    self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    managedObjectContext = [[NSManagedObjectContext alloc] init];
    [managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    
    if (!dataBaseExists) {
        [self fillDB];
    }
    
	return self;
}

-(void) changeTableToUnsynchronizedState:(NSString *)entityName {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:entityName
                                   inManagedObjectContext:self.managedObjectContext]];
    
    NSError *error = nil;
    NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        $l(@"request error - %@",[error localizedDescription]);
        return ;
    }
    
    for (NSManagedObject *managedObject in fetchedResults) {
        [managedObject performSelector:@selector(setIsSynchronized:) withObject:[NSNumber numberWithBool:NO]];
    }
}


-(void) deleteEntityWithUnsynchronizedState:(NSString *)entityName {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:entityName
                                   inManagedObjectContext:self.managedObjectContext]];
    [request setPredicate:[NSPredicate predicateWithFormat:@"SELF.isSynchronized = %@", [NSNumber numberWithBool:NO]]];
    NSError *error = nil;
    NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        $l(@"request error - %@",[error localizedDescription]);
        return ;
    }
    
    for (NSManagedObject *managedObject in fetchedResults) {
        [self.managedObjectContext deleteObject:managedObject];
    }
}

#pragma mark - Interface methods


-(void) startSynchronization {
    [self changeTableToUnsynchronizedState:[[CDCategories class] description]];
    [self changeTableToUnsynchronizedState:[[CDProducts class] description]];
    [self changeTableToUnsynchronizedState:[[CDCollections class] description]];
    [self changeTableToUnsynchronizedState:[[CDImages class] description]];
    
    ProductManager *productManager = [ProductManager sharedInstance];
    
    for (Categories *category in productManager.categories) {
        CDCategories *cdCategory = [self categoryWithIdent:category.ident];
        
        if (!cdCategory) {
            cdCategory = [CDCategories categoryWithCategoryInfo:category
                                                      inContext:self.managedObjectContext];
        } else {
            [cdCategory updateWithCategoryInfo:category
                                     inContext:self.managedObjectContext];
        }
    }
    
    for (Collections *collection in productManager.collections) {
        CDCollections *cdCollection = [self collectionWithIdent:collection.ident];
        
        if (!cdCollection) {
            cdCollection = [CDCollections colletionWithCollectionInfo:collection
                                                            inContext:self.managedObjectContext];
        } else {
            [cdCollection updateWithCollecionInfo:collection
                                        inContext:self.managedObjectContext];
        }
        CDImages *image = nil;// [self imageWithIdent:collection.image_id];
        if (image) {
            [cdCollection addImageObject:image];
        } else {
            $l(@"image for collection - %@ not found", collection.name);
        }
        
        CDCategories *category = [self categoryWithIdent:collection.category_id];
        if (category) {
            [cdCollection addCategoryObject:category];
        } else {
            $l(@"category for collection - %@ not found", collection.name);
        }
    }

    for (Products *product in productManager.products) {
        CDProducts *cdProduct = [self productWithIdent:product.ident];
        
        if (!cdProduct) {
            cdProduct = [CDProducts productWithProductInfo:product
                                                 inContext:self.managedObjectContext];
        } else {
            [cdProduct updateWithProductInfo:product
                                   inContext:self.managedObjectContext];
        }
        
        CDCollections *collection = [self collectionWithIdent:product.collection_id];
        if (collection) {
            cdProduct.collection = collection;
        } else {
            $l(@"collection for product - %@ not found", product.name);
        }
        
        for (NSString *imageUrl in product.images) {
            CDImages *image = [self imageWithUrl:imageUrl];
            Images *imageInfo = [[Images alloc] init];
            imageInfo.url = imageUrl;
            imageInfo.ident = [NSNumber numberWithInteger:1];
            if (!image) {
                image = [CDImages imageWithImageInfo:imageInfo inContext:self.managedObjectContext];
            } else {
                [image updateWithImageInfo:imageInfo inContext:self.managedObjectContext];
            }
            
            NSString *fileName = [[productManager imageFolder] stringByAppendingPathComponent:[Utils md5:imageUrl]];
            if ([[NSFileManager defaultManager] fileExistsAtPath:fileName]) {
                [cdProduct addImagesObject:image];
            } else {
                [self.managedObjectContext deleteObject:image];
            }
        }
    }

    [self deleteEntityWithUnsynchronizedState:[[CDCategories class] description]];
    [self deleteEntityWithUnsynchronizedState:[[CDProducts class] description]];
    [self deleteEntityWithUnsynchronizedState:[[CDCollections class] description]];
    [self deleteEntityWithUnsynchronizedState:[[CDImages class] description]];

    [self saveMainContext];
}


-(void) saveManagedObjectContext:(NSManagedObjectContext *)context {
	if (!context || ![context hasChanges]) {
        return;
    }
    
    NSError *error;
    BOOL saved = [context save:&error];
    
    if (!saved) {
		NSDictionary *dict  = [error userInfo];
		NSArray *detailedErrors = [dict objectForKey:NSDetailedErrorsKey];
		if (detailedErrors) {
			$l(@"Context was not saved because: ");
			for (NSError *detailedError in detailedErrors) {
				$l(@"Error : %@", [detailedError userInfo]);
			}
		} else {
            $l(@"Could not save DB: %@", [error userInfo]);
		}
		
        $l(@"Could not save data to persistent store. Error description: %@", [error localizedDescription]);
	}
}


-(void) saveMainContext {
	[self saveManagedObjectContext:self.managedObjectContext];
}


-(void) dropDataBase {
    NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] lastObject];
	NSURL *storeURL = store.URL;
	NSError *error = nil;
	
	[persistentStoreCoordinator removePersistentStore:store
												error:&error];
    if (error) {
        $l(@"Can't remove persistent store: %@", [error localizedDescription]);
        error = nil;
    }
	
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    if ([fileManager fileExistsAtPath:storeURL.path]) {
        [fileManager removeItemAtPath:storeURL.path
                                error:&error];
	}
    
    if (error) {
        $l(@"Can't remove persistent store file: %@", [error localizedDescription]);
        error = nil;
    }
    
	[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
											 configuration:nil
													   URL:storeURL
												   options:nil
													 error:&error];
    
    if (error) {
        $l(@"Can't create persistent store: %@", [error localizedDescription]);
        error = nil;
    }
}


-(void) addObserverForManagedObjectContext:(NSManagedObjectContext *)context {
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(managedObjectContextDidSave:)
												 name:NSManagedObjectContextDidSaveNotification
											   object:context];
}


-(void) removeObserverForManagedObjectContext:(NSManagedObjectContext *)context {
	[[NSNotificationCenter defaultCenter] removeObserver:self
													name:NSManagedObjectContextDidSaveNotification
												  object:context];
}


-(CDCategories *) categoryWithIdent:(NSNumber *)ident {
    CDCategories *selectedCategory = (CDCategories *)[self managedObjectWith:ident
                                                                  entityName:[[CDCategories class] description]];
    
    return selectedCategory;
}


-(CDImages *) imageWithUrl:(NSString *)url {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:[[CDImages class] description]
                                   inManagedObjectContext:self.managedObjectContext]];
    [request setPredicate:[NSPredicate predicateWithFormat:@"SELF.url = %@", url]];
    [request setFetchLimit:1];
    
    NSError *error = nil;
    NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        $l(@"request error - %@",[error localizedDescription]);
    }
    
    return [fetchedResults lastObject];
}


-(CDCollections *) collectionWithIdent:(NSNumber *)ident {
    CDCollections *selectedCollection = (CDCollections *)[self managedObjectWith:ident
                                                                      entityName:[[CDCollections class] description]];
    return selectedCollection;
}


-(CDProducts *) productWithIdent:(NSNumber *)ident {
    CDProducts *selectedProduct = (CDProducts *)[self managedObjectWith:ident
                                                             entityName:[[CDProducts class] description]];
    return selectedProduct;
}


-(NSArray *) categories {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:[[CDCategories class] description]
                                   inManagedObjectContext:self.managedObjectContext]];
    
    NSError *error = nil;
    NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        $l(@"request error - %@",[error localizedDescription]);
    }
    
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"ident"
                                                  ascending:YES];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    
    return [fetchedResults sortedArrayUsingDescriptors:sortDescriptors];
}


#pragma mark - Private methods

-(NSManagedObject *) managedObjectWith:(NSNumber *)ident entityName:(NSString *)entityName {
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:entityName
                                   inManagedObjectContext:self.managedObjectContext]];
    [request setPredicate:[NSPredicate predicateWithFormat:@"SELF.ident = %@", ident]];
    [request setFetchLimit:1];
    
    NSError *error = nil;
    NSArray *fetchedResults = [self.managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        $l(@"request error - %@",[error localizedDescription]);
    }
    
    return [fetchedResults lastObject];
}


#pragma mark - Properties methods

-(NSPersistentStoreCoordinator *) persistentStoreCoordinator {
    if (persistentStoreCoordinator) {
        return persistentStoreCoordinator;
    }
	
    NSString *dbFilePath = [[Utils applicationCacheDirectory] stringByAppendingPathComponent: @"fr.sqlite"];
    
    NSURL *storeUrl = [NSURL fileURLWithPath:dbFilePath];
	
	NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                                  configuration:nil
                                                                                            URL:storeUrl
                                                                                        options:nil
                                                                                          error:&error];
	
	if (!persistentStore) {
		NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
								 [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
								 [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
		
		persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
																   configuration:nil
																			 URL:storeUrl
																		 options:options error:&error];
		
		$epicfail(!persistentStore, @"Could not load database. Error description: %@", [error localizedDescription]);
    }
    
    
    return persistentStoreCoordinator;
}


-(void) fillDB {
}


#pragma mark - Notification observers

-(void) managedObjectContextDidSave:(NSNotification *)notification {
	[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}


@end